/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package provider.impl.coalescent.lc;

import coalescent.data.KC64Data;
import coalescent.model.KC64;
import coalescent.provider.Providers;
import coalescent.statistic.KC64_AC;
import commons.core.LikelihoodComputer;
import commons.core.MultiLHoodComputer;
import commons.provider.ProviderException;
import commons.provider.impl.BaseProviderImpl;
import org.apfloat.Apfloat;

import java.math.BigDecimal;

import static commons.util.ApfloatUtil.*;
import static org.apfloat.ApfloatMath.pow;

/**
 * @since
 * @version
 * @author Susanta Tewari
 * @history Created on 2/20/13.
 */
public class PImpl_LC_KC64_ESF extends BaseProviderImpl<LikelihoodComputer<KC64Data.DEFAULT>>
        implements Providers.KC64LHoodProvider {

    @Override
    public LikelihoodComputer<KC64Data.DEFAULT> create() throws ProviderException {

        return new LikelihoodComputer<KC64Data.DEFAULT>() {

            @Override
            public BigDecimal computeLogLHood(final KC64Data.DEFAULT kc64Data)
                    throws InterruptedException {
                return ESF.prob(kc64Data);
            }
        };
    }

    @Override
    public Class<KC64Data.DEFAULT> getDataClass() {
        return KC64Data.DEFAULT.class;
    }

    @Override
    public Class<KC64> getModelClass() {
        return KC64.class;
    }

    static class ESF {

        static BigDecimal prob(final KC64Data.DEFAULT kc64Data) {

            final KC64_AC an   = new KC64_AC(kc64Data);
            final Integer n    = an.getN();
            final Double theta = an.getModel().getMutationRate();


            // result
            Apfloat result = new Apfloat(factorial(n)).divide(new Apfloat(ascendingFactorial(theta,
                                 n)));
            final int[] a = an.getAVector();

            for (int j = 1; j <= a.length; j++) {

                final int aj = a[j - 1];

                if (aj == 0) continue;

                final Apfloat term_0   = new Apfloat(theta).divide(new Apfloat(j));
                final Apfloat term_1   = pow(term_0, aj);
                final Apfloat term_2   = new Apfloat(factorial(aj));
                final Apfloat new_term = term_1.divide(term_2);

                result = result.multiply(new_term);
            }

            return toBigDecimal(result);
        }
    }

    /**
     * @version version
     * @since 1.4.0
     * @history Created on 05/15/2013
     * @author Susanta Tewari
     */
    public static class Multi
            extends BaseProviderImpl<MultiLHoodComputer<KC64, KC64Data.DEFAULT, KC64Data.DEFAULT>>
            implements Providers.KC64MultiLHoodProvider {

        @Override
        public MultiLHoodComputer<KC64, KC64Data.DEFAULT, KC64Data.DEFAULT> create()
                throws ProviderException {
            return MultiLHoodComputer.ofDefault(new PImpl_LC_KC64_ESF().create());
        }

        @Override
        public Class<KC64Data.DEFAULT> getDataClass() {
            return KC64Data.DEFAULT.class;
        }

        @Override
        public Class<KC64> getModelClass() {
            return KC64.class;
        }
    }
}
